﻿using Cms.Core.Infrastructure.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;


namespace Cms.Core.Infrastructure.Helps
{
    public static class FilterHelp
    {
        private static readonly string operate = "=,<=,<,>,>=,!=,Constants,NotContains";
        private static readonly Dictionary<string, Func<Expression, Expression, Expression>> ExpressionDic =
            new Dictionary<string, Func<Expression, Expression, Expression>>
            {
                {
                    "=",Expression.Equal
               },

                {
                    "<=",Expression.LessThanOrEqual

                },
                {
                   "<",Expression.LessThan

                },
                {
                    ">",Expression.GreaterThan

                },
                {
                    ">=",Expression.GreaterThanOrEqual

                },
                {
                    "!=",Expression.NotEqual

                },
                {
                    "Constants".ToLower(), (left, right) =>
                    {
                        if (left.Type != typeof(string))
                        {
                            throw new NotSupportedException("“Constants”比较方式只支持字符串类型的数据");
                        }

                      return Expression.Call(left, typeof(string).GetMethod("Contains", new []{ typeof(string) }), right);

                    }
                }, {
                    "NotContains".ToLower(),
                    (left, right) =>
                    {
                        if (left.Type != typeof(string))
                        {
                            throw new NotSupportedException("“NotContains”比较方式只支持字符串类型的数据");
                        }
                        return Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new[] { typeof(string) }), right));
                    }
                }
               
                //,
                //{
                //    FilterOperate.In, (left, right) =>
                //    {
                //        return left.Type != typeof(string)?null:Expression.Call(typeof(Enumerable),"Contains",new []{left.Type},right,left);
                //    }

                //}
            };

        /// <summary>
        /// 创建linq表达示的body部分
        /// </summary>
        private static Expression GenerateBody(this ParameterExpression param, FilterInfo filterObj)
        {
            if ((filterObj.Value ?? string.Empty).ToString().IsNullOrWhiteSpace())
            {
                return Expression.Constant(true);
            }
            //if (filterObj.Value == null || filterObj.Value == string.Empty)
            //{
            //    return Expression.Constant(true);
            //}
            var expression = GetPropertyLambdaExpression(param, filterObj);

            var constant = ChangeTypeToExpression(filterObj, expression.Body.Type);

            return ExpressionDic[filterObj.Operate.ToLower()](expression.Body, constant);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="param"></param>
        /// <param name="filterObj"></param>
        /// <returns></returns>
        private static LambdaExpression GetPropertyLambdaExpression(ParameterExpression param, FilterInfo filterObj)
        {
            var type = param.Type;
            var property = type.GetProperty(filterObj.Field);
            Expression propertyAccess = Expression.MakeMemberAccess(param, property);
            return Expression.Lambda(propertyAccess, param);
        }


        /// <summary>
        /// 类型转换
        /// </summary>
        /// <param name="filterObj"></param>
        /// <param name="type"></param>
        /// <returns></returns>

        private static Expression ChangeTypeToExpression(FilterInfo filterObj, Type type)
        {
            Type unNullableType = type.GetUnNullableType();

            //if (baseType.IsArray|| filterObj.Value is Enumerable)
            //{

            //    return Expression.Constant(filterObj.Value, baseType);
            //}
            object value = filterObj.Value.AsTo(unNullableType);

            return Expression.Constant(value, type);
        }



        private static Expression<Func<T, bool>> GetExpression<T>(List<FilterInfo> filters)
        {


            ParameterExpression parameter = Expression.Parameter(typeof(T), "m");
            Expression body = Expression.Constant(true);
            foreach (var filterObj in filters)
            {


                if (!operate.Contains(filterObj.Operate))
                {
                    throw new Exception($"过滤条件目前只支持{operate}");
                }
                if (filterObj.Connect.Equals("and", StringComparison.OrdinalIgnoreCase))
                {
                    body = body.AndAlso(GenerateBody(parameter, filterObj));

                }
                else if (filterObj.Connect.Equals("ord", StringComparison.OrdinalIgnoreCase))
                {
                    body = body.OrElse(GenerateBody(parameter, filterObj));
                }
                else
                {
                    throw new Exception("没有找到对应符！！");
                }
            }

            return Expression.Lambda<Func<T, bool>>(body, parameter);
        }



        /// <summary>
        /// 创建完整的lambda
        /// </summary>
        private static LambdaExpression GenerateLambda(this ParameterExpression param, Expression body)
        {
            //c=>c.XXX=="XXX"
            return Expression.Lambda(body, param);
        }

        private static Expression<Func<T, bool>> GenerateTypeLambda<T>(this ParameterExpression param, Expression body)
        {
            return (Expression<Func<T, bool>>)(param.GenerateLambda(body));
        }

        private static Expression AndAlso(this Expression expression, Expression expressionRight)
        {
            return Expression.AndAlso(expression, expressionRight);
        }

        private static Expression OrElse(this Expression expression, Expression expressionRight)
        {
            return Expression.OrElse(expression, expressionRight);
        }

        //private static Expression Or(Expression expression, Expression expressionRight)
        //{
        //    return Expression.Or(expression, expressionRight);
        //}

        //private static Expression And(Expression expression, Expression expressionRight)
        //{
        //    return Expression.And(expression, expressionRight);
        //}

        /// <summary>
        /// 检查过滤字段是否存在
        /// </summary>
        private static void CheckFilterFieldIsExist<T>(List<FilterInfo> filters)
        {
            var type = typeof(T);
            List<string> list = new List<string>();
            var propertys = type.GetProperties();
            var fields = filters.Select(o => o.Field).ToList();
            var propertyNames=   propertys.Where(o => !fields.Contains(o.Name)).Select(o=>o.Name).ToList();

            if (propertyNames?.Count > 0)
            {
                throw new Exception($"{type}实体下，没有找到要查询对应的{propertyNames.ToJoin()}属性，无法生成对应Expression");
            }

            //filters.ForEach(o =>
            //{

            //    var field = o.Field;
            //    var propertyName = type.GetProperty(field);
            //    if (propertyName == null)
            //    {
            //        list.Add(field);
            //    }
 
            //});

            //if (list?.Count() > 0)
            //{
            //    throw new  Exception($"{type}实体下，没有找到要查询对应的{list.ToJoin()}属性，无法生成对应Expression");
            //}
      
        }

        /// <summary>
        /// 转成条件表达目录树
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filters"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> ToFilterExpression<T>(List<FilterInfo> filters)
        {
            //CheckFilterFieldIsExist<T>(filters);
            return GetExpression<T>(filters);
        }



    }
}
